/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package hxg.portmgr.core.dao.impl.jaxb;

import hxg.portmgr.core.dao.LineDAO;
import hxg.portmgr.core.device.Device;
import hxg.portmgr.core.device.intf.Intf;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import hxg.portmgr.core.line.Line;
import hxg.portmgr.core.dao.DaoSource;

/**
 *
 * @author hxg
 */
public class LineDaoJaxbImpl implements LineDAO {

    private static final Logger logger = Logger.getLogger(LineDaoJaxbImpl.class.getName());
    final static File f = new File("data\\line.xml");
    LinkedHashMap<Intf, Line> Intf2LineMap = new LinkedHashMap();
    boolean ready = false;
    //private LinkedList<LineData> linedata = new LinkedList<>();

    public LineDaoJaxbImpl() {
        if (!f.exists()) {
            try {
                f.getParentFile().mkdirs();
                f.createNewFile();
                save();
            } catch (IOException ex) {
            }
        }
        //load();
    }

    @Override
    public Line connect(Intf inf1, Intf inf2) {
        Line line = new Line();
        line.setName(inf1.getDevice().getChsName() + "[" + inf1.toString() + "(" + inf1.getDesc() + ")] to " + inf2.getDevice().getChsName() + "[" + inf2.toString() + "(" + inf2.getDesc() + ")]");
        line.ConnectToIntf(inf1).ConnectToIntf(inf2);
        Intf2LineMap.put(inf1, line);
        Intf2LineMap.put(inf2, line);
        save();
        return line;
    }

    @Override
    public Line connect(Intf inf1, String connPoint) {
        Line line = new Line();
        line.setName(connPoint + "@" + inf1.getDevice().getChsName() + "[" + inf1.toString() + "(" + inf1.getDesc() + ")]");
        line.ConnectToIntf(inf1).ConnectToNode(connPoint);
        Intf2LineMap.put(inf1, line);
        save();
        return line;
    }

    @Override
    public Line getLine(Intf inf) {
        return Intf2LineMap.get(inf);
    }

    @Override
    public boolean isConnected(Intf inf) {
        return Intf2LineMap.containsKey(inf);
    }

    @Override
    public Intf getOtherIntf(Intf inf) {
        //LinkedHashMap<Line, LinkedList<Intf>> mp = getLinetoIntfMap();
        Line line = getLine(inf);
        for (Intf intf : line.getIntfs()) {
            if (!intf.equals(inf)) {
                return intf;
            }
        }
        return null;
    }

    @Override
    public String getOtherSideName(Intf inf) {
        Line line = getLine(inf);
        if (line != null) {
            String lineName = line.getName();
            return lineName.substring(lineName.indexOf("-") + 1);
        } else {
            return "";
        }
    }

    /**
     * 接触线路连接时会自动接触另一端的连接
     *
     * @param inf
     * @return
     */
    @Override
    public Line disConnect(Intf inf) {

        Intf otherIntf = getOtherIntf(inf);
        Line line = Intf2LineMap.remove(inf);
        line.disConnectToIntf(inf);
        if (otherIntf != null) {
            line.disConnectToIntf(otherIntf);
            //Intf2LineMap.remove(otherIntf);
        }
        Intf2LineMap.remove(otherIntf);
        return line;
    }

    @Override
    public final void save() {
        encode(f);
    }

    @Override
    public final void load() {
        Thread t = new Thread(new Runnable() {

            @Override
            public void run() {
                logger.log(Level.INFO, "开始载入线路信息");
                decode(f);
                logger.log(Level.INFO, "正常,载入了线路{0}对", Intf2LineMap.values().size());
                ready = true;
            }
        });
        t.setDaemon(true);
        t.start();
    }

    private void encode(File f) {
        OutputStream os = null;
        try {
            Collection<Line> lines = Intf2LineMap.values();
            Set<Line> lineset = new HashSet<Line>(lines);
            List<LineData> linedatas = new LinkedList();
            for (Line line : lineset) {
                //Line line = entry.getKey();
                Set<Intf> infs = line.getIntfs();
                String[] linedataStrs = new String[2];
                Iterator<Intf> iter = infs.iterator();
                if (infs.size() == 1) {
                    Intf inf1 = iter.next();
                    linedataStrs[0] = inf1.getDevice().getId() + "-" + inf1.getIndex();
                    linedataStrs[1] = "@" + line.getName().substring(0, line.getName().indexOf("@"));
                } else if (infs.size() == 2) {
                    Intf inf1 = iter.next();
                    linedataStrs[0] = inf1.getDevice().getId() + "-" + inf1.getIndex();
                    Intf inf2 = iter.next();
                    linedataStrs[1] = inf2.getDevice().getId() + "-" + inf2.getIndex();
                }
                LineData data = new LineData();
                data.IntfData = linedataStrs;
                linedatas.add(data);
            }
            os = new FileOutputStream(f);
            //pw = new PrintWriter(f);
            JAXBContext jc = JAXBContext.newInstance(LineDataListBean.class);
            Marshaller m = jc.createMarshaller();
            m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
            //m.setProperty(Marshaller.JAXB_ENCODING, "gb2312");
            LineDataListBean dmlist = new LineDataListBean();
            dmlist.linedatas.addAll(linedatas);
            m.marshal(dmlist, os);
            //pw.flush();
        } catch (Exception ex) {
            logger.log(Level.WARNING, ex.getMessage());
        } finally {
            try {
                os.close();
            } catch (IOException ex) {
            }
        }


    }

    private void decode(File f) {
        try {
            JAXBContext jc = JAXBContext.newInstance(LineDataListBean.class);
            Unmarshaller u = jc.createUnmarshaller();
            LineDataListBean list = (LineDataListBean) u.unmarshal(f);
            List<LineData> linedatas = list.linedatas;
            for (LineData data : linedatas) {
                String infinfo = data.IntfData[0];
                long id ;
                try {
                    id=Long.parseLong(infinfo.substring(0, infinfo.indexOf("-")));
                } catch (Exception e) {
                    id = 0;
                }
                String ifidx = infinfo.substring(infinfo.indexOf("-") + 1);
                //System.out.println(ip + "    " + ifidx);
                Intf inf = (DaoSource.getDeviceDao().getDeviceById(id)).getIntf(ifidx);
                String infinfoB = data.IntfData[1];
                if (infinfoB.startsWith("@")) {
                    connect(inf, infinfoB);
                } else {
                    long idB=0;
                    try{
                        idB = Long.parseLong(infinfoB.substring(0, infinfoB.indexOf("-")));
                    }catch(Exception e){
                        logger.log(Level.WARNING,"Line 持久化反转时出现问题，线路实例化失败，问题在于{0\n,异常：{1}", new Object[]{infinfoB,e});
                    }
                    String ifidxB = infinfoB.substring(infinfoB.indexOf("-") + 1);
                    Intf infB = (DaoSource.getDeviceDao().getDeviceById(idB)).getIntf(ifidxB);
                    connect(inf, infB);
                }
            }
        } catch (JAXBException ex) {
            Logger.getLogger(LineDaoJaxbImpl.class.getName()).log(Level.SEVERE, null, ex);
            //return new ArrayList();
        }
    }

    @Override
    public Collection<Line> getLines() {
        return Intf2LineMap.values();
    }

    @Override
    public Set<Line> getLineOfDevice(Device dev) {
        Collection<Line> alllines = getLines();
        Set<Line> lines = new LinkedHashSet();
        for (Line line : alllines) {
            for (Intf inf : line.getIntfs()) {
                if (inf.getDevice().equals(dev)) {
                    lines.add(line);
                }
            }
        }
        return lines;
    }

    @Override
    public Set<Device> getLinkedDevice(Line line) {
        LinkedHashSet<Device> devs = new LinkedHashSet();
        for (Intf inf : line.getIntfs()) {
            devs.add(inf.getDevice());
        }
        return devs;
    }

    @Override
    public Set<Device> getLinkedDevice(Device dev) {
        Set<Device> devs = new HashSet();
        Set<Line> lines = getLineOfDevice(dev);
        for (Line line : lines) {
            devs.addAll(getLinkedDevice(line));
        }
        return devs;
    }

    @Override
    public boolean isReady() {
        return ready;
    }

    @XmlAccessorType(XmlAccessType.FIELD)
    @XmlType(name = "", propOrder = {})
    @XmlRootElement(name = "LineTable")
    private static class LineDataListBean {

        @XmlElement
        List<LineData> linedatas = new LinkedList();
    }

    @XmlAccessorType(XmlAccessType.FIELD)
    @XmlType(name = "", propOrder = {})
    @XmlRootElement(name = "Line")
    private static class LineData {

        @XmlElement
        public String[] IntfData;
    }
}
